// ----------------
// Project:
// ESA Elevator
// ----------------
//
// Description:
// ----------------
// floor_request_if.v testbench
//
// Version History:
// ----------------
// 140116:

`timescale 1ns / 1ps

module floor_request_if_tb_public;

  //****************** SIMULATION PARAMETERS *******************//  
  localparam CLK_PERIOD  =  50; // [ns] -> 20 MHz
  localparam NUM_FLOORS  =  99; // number of floors
  //***********************************************************//
  

  //*************** DERIVED SIMULATION PARAMETERS *************// 
  localparam FLOOR_BITS            = min_1(ceil_log2(NUM_FLOORS-1));
  //***********************************************************//
  
  
  localparam UP   = 1;
  localparam DOWN = 0;
  

  //********************* MODULE INPUTS ***********************// 
  reg                          CLK;
  reg                          RESET;
  reg                          HALTED;
  reg [(FLOOR_BITS-1)      :0] CURRENT_FLOOR;      
  reg [((NUM_FLOORS*2)-1)  :0] FLOOR_REQUEST; 
  //***********************************************************//  


  //********************* MODULE OUTPUTS **********************//     
  wire  [((NUM_FLOORS*2)-1):0] REQUESTED_FLOORS;      
  wire  [((NUM_FLOORS*2)-1):0] ENLIGHT_REQUEST_BUTTONS; 
  //***********************************************************// 
  

  //******************* UUT INSTANTIATION *********************// 
  floor_request_if #(.FLOORS     (NUM_FLOORS), 
                     .FLOOR_BITS (FLOOR_BITS)) 
  
      FLOOR_REQUEST_IF_i (
                         .CLK               (CLK),
                         .RESET             (RESET),
       
                         .CURRENT_FLOOR     (CURRENT_FLOOR),
                         .HALTED            (HALTED),
                         
                         .ENLIGHT_BUTTONS   (ENLIGHT_REQUEST_BUTTONS),
                         
                         .FLOOR_REQUEST_IN  (FLOOR_REQUEST),
                         .FLOOR_REQUEST_OUT (REQUESTED_FLOORS));                           
  //***********************************************************//


  //***************** SIMULATION VARIABLES ********************// 
  integer i;
  reg  [((NUM_FLOORS*2)-1):0] enlight_request_button;
  reg  [((NUM_FLOORS*2)-1):0] requested_floor;   
  //***********************************************************//
  

  //******************* 20 MHz CLOCK SIGNAL *******************//   
  always begin 
    #(CLK_PERIOD) CLK = ~CLK;
  end
  //***********************************************************// 


  //********************* TEST INITIATION *********************//  
  initial begin
    enlight_request_button   = 0;
    requested_floor          = 0;
    // Initialize Inputs
    CLK                      = 0;
    RESET                    = 1;
    FLOOR_REQUEST            = 0;
    HALTED                   = 0;
    CURRENT_FLOOR            = 0;

    // Wait 125 ns for global reset to finish
    #(3*CLK_PERIOD/2);
    RESET = 0;
    $display("************ STARTING SIMULATION ************");  
    #(5*CLK_PERIOD);
    //--------------------------
    // SELECT FLOORS 
    //--------------------------
    request_floor(UP, 0);
    #(CLK_PERIOD);
    request_floor(DOWN, 1);
    #(CLK_PERIOD);
    request_floor(UP, 2);
    #(CLK_PERIOD);
    request_floor(UP, 3);
    #(CLK_PERIOD);
    request_floor(DOWN, 4);  
    #(CLK_PERIOD);     
    request_floor(UP, 1);
    #(CLK_PERIOD);
    request_floor(DOWN, 2);
    #(CLK_PERIOD);
    request_floor(DOWN, 3);
    #(5*CLK_PERIOD);  
    //--------------------------
    // HALT FLOORS 
    //--------------------------
    halt_floor(4);
    #(CLK_PERIOD);
    halt_floor(3);
    #(CLK_PERIOD);
    halt_floor(2);
    #(CLK_PERIOD);
    halt_floor(1);
    #(CLK_PERIOD);
    halt_floor(0);        
    #(5*CLK_PERIOD);
    $display("************ SIMULATION COMPLETE ************");
    $finish;
    //-------------------------- 
  end
  //***********************************************************// 
initial begin
  #(100000*CLK_PERIOD)
  $display("************ SIMULATION KILLED BECAUSE OF ERROR ************");
  $finish;
end


  //********************* SIMULATION TASKS ********************//
  task request_floor;
     input                  direction;
     input [FLOOR_BITS-1:0] floor;
     begin
       FLOOR_REQUEST[2*floor+direction] = 1'b1;
       #(CLK_PERIOD);
       FLOOR_REQUEST[2*floor+direction] = 1'b0;
     end
  endtask
  

  task halt_floor;
     input [FLOOR_BITS-1:0] floor;
     begin
       CURRENT_FLOOR = floor;
       HALTED        = 1;
       #(CLK_PERIOD);
       HALTED        = 0;    
    end
  endtask  
  //***********************************************************// 
  

  //****************** VISUALISATION PROCESS ******************//
  always@(ENLIGHT_REQUEST_BUTTONS) begin    
    for(i=0;i<2*NUM_FLOORS;i=i+1) begin
      if(ENLIGHT_REQUEST_BUTTONS[i] && ~enlight_request_button[i]) begin
        enlight_request_button[i] = 1;
        if (((i+1)/2) == (i/2)) begin
          $display("Enlight DOWN button @ floor %d", i/2);        
        end
        else begin
          $display("Enlight UP button @ floor %d", i/2);  
        end
      end
      else if(~ENLIGHT_REQUEST_BUTTONS[i] && enlight_request_button[i]) begin
        enlight_request_button[i] = 0;
        if (((i+1)/2) == (i/2)) begin
          $display("Disable light DOWN button @ floor %d", i/2);        
        end
        else begin
          $display("Disable light UP button @ floor %d", i/2);  
        end      
      end      
    end     
  end
  
  
  always@(REQUESTED_FLOORS) begin
    for(i=0;i<2*NUM_FLOORS;i=i+1) begin
      if(REQUESTED_FLOORS[i] && ~requested_floor[i]) begin
        requested_floor[i] = 1;
        if (((i+1)/2) == (i/2)) begin
          $display("SELECT DOWN button @ floor %d", i/2);        
        end
        else begin
          $display("SELECT UP button @ floor %d", i/2);  
        end
      end
      else if(~REQUESTED_FLOORS[i] && requested_floor[i]) begin  
        requested_floor[i] = 0;
        if (((i+1)/2) == (i/2)) begin
          $display("Request fullfilled DOWN button @ floor %d", i/2);        
        end
        else begin
          $display("Request fullfilled UP button @ floor %d", i/2);  
        end
      end      
    end  
  end
  //***********************************************************// 


  //******************* PARAMETER FUNCTIONS *******************//
  //ceil of the log base 2
  function integer ceil_log2;
    input [31:0] value;
    for (ceil_log2=0; value>0; ceil_log2=ceil_log2+1)
      value = value>>1;
  endfunction
  
  // value cannot be less than 1
  function integer min_1;
    input [31:0] value;
    if (value == 0)
      min_1 = 1;
    else
      min_1 = value;
  endfunction
  //***********************************************************// 
        
endmodule

